package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.cell.CellEditor;
import com.tbit.common.constant.OperationPlatform;
import com.tbit.common.entity.UserOpLog;
import com.tbit.uqbike.constant.MapConstant;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.business.Point;
import com.tbit.uqbike.object.business.UserInfo;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.pojo.statistics.ExportProhibitArea;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.common.entity.view.Result;
import com.tbit.uqbike.service.base.CalibrateService;
import com.tbit.uqbike.service.base.ParkPointService;
import com.tbit.uqbike.service.business.ProhibitAreaService;
import com.tbit.uqbike.webmanager.business.user_op_log.service.UserOpLogService;
import com.tbit.uqbike.webmanager.controller.BaseController;
import com.tbit.uqbike.webmanager.dao.core.ProhibitAreaDao;
import com.tbit.uqbike.webmanager.util.Assert;
import com.tbit.uqbike.webmanager.util.StringUtils;
import com.tbit.uqbike.webmanager.util.adaPay.AdaPayUtil;
import com.tbit.uqbike.webmanager.util.excel.ExcelUtil;
import com.tbit.utils.GeoUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: chen
 * @Description: 禁停区域
 * @Date: 创建于 11:57 2020/3/31
 */
@Service("prohibitAreaService")
public class ProhibitAreaServiceImpl implements ProhibitAreaService {

    private static final Logger log = LoggerFactory.getLogger(ProhibitAreaServiceImpl.class);
    @Autowired
    private ProhibitAreaDao prohibitAreaDao;
    @Autowired
    private UserOpLogService userOpLogService;
    @Autowired
    private ParkPointService parkPointService;
    @Autowired
    private CalibrateService calibrateService;

    @Override
    public List<ProhibitArea> queryPage(Integer accountId, String name, Integer pageNO, Integer rowCount) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "%" + name + "%");
        map.put("start", (pageNO - 1) * rowCount);
        map.put("rowCount", rowCount);
        map.put("accountId", accountId);
        return prohibitAreaDao.queryPage(map);
    }

    @Override
    public Integer queryCount(Integer accountId, String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "%" + name + "%");
        map.put("accountId", accountId);
        return prohibitAreaDao.queryCount(map);
    }

    @Override
    public void insert(ProhibitArea prohibitArea) {
        prohibitArea.setProhibitAreaType(1);
        checkCenter(prohibitArea);
        prohibitAreaDao.insert(prohibitArea);
    }

    @Override
    public void edit(ProhibitArea prohibitArea) {
        ProhibitArea areaDB = prohibitAreaDao.getById(prohibitArea.getProhibitAreaId());

        checkCenter(prohibitArea);
        prohibitAreaDao.edit(prohibitArea);

        // 插入用户操作日志
        UserInfo loginInfo = BaseController.fetchUserInfo();
        userOpLogService.insertSingle(UserOpLog.builder()
                .accountId(areaDB.getAccountId())
                .opUser(loginInfo.getName())
                .opUserPhone(loginInfo.getPhone())
                .operation("编辑禁停区")
                .opPlatform(OperationPlatform.MANAGER.code)
                .opTime(LocalDateTime.now())
                .result(true)
                .build());
    }

    @Override
    public void del(Integer prohibitAreaId) {
        prohibitAreaDao.del(prohibitAreaId);
    }

    @Override
    public List<ProhibitArea> getByArea(Integer accountId, Double lo, Double la, Integer radius) {
        Double area = 0.00001D * radius;

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("accountId", accountId);
        params.put("minLo", lo - area);
        params.put("maxLo", lo + area);
        params.put("minLa", la - area);
        params.put("maxLa", la + area);
        return prohibitAreaDao.getByArea(params);
    }

    @Override
    public List<ProhibitArea> getAll(Integer accountId) {
        return prohibitAreaDao.getAll(accountId);
    }

    @Override
    public void batchImport(MultipartFile file, Integer accountId, LoginInfo loginInfo, Integer mapType) {
        List<BatchAddProhibitArea> addProhibitAreaList = ExcelUtil.readAll(file, BatchAddProhibitArea.class);
        List<ProhibitArea> prohibitAreaList = new ArrayList<>();
        for (BatchAddProhibitArea addProhibitArea : addProhibitAreaList) {
            Assert.notNull(addProhibitArea.getProhibitAreaName(), "站点名称不能为空");
            Assert.notNull(addProhibitArea.getAllowRange(), "误差范围不能为空");
            Assert.notNull(addProhibitArea.getPoints(), "经纬度不能为空");

            parkPointService.checkPoints(addProhibitArea.getPoints());

            ProhibitArea prohibitArea = new ProhibitArea();
            prohibitArea.setAccountId(accountId);
            prohibitArea.setName(addProhibitArea.getProhibitAreaName());
            prohibitArea.setAllowRange(addProhibitArea.getAllowRange());
            prohibitArea.setPoints(addProhibitArea.getPoints());

            if (Objects.equals(MapConstant.MAP_ORI, mapType)) {
                calibrateService.calProhibitArea(prohibitArea, MapConstant.MAP_ORI, MapConstant.MAP_GOOGLE);
            } else {
                calibrateService.calProhibitArea(prohibitArea, MapConstant.MAP_GOOGLE, MapConstant.MAP_ORI);
            }
            prohibitArea.setProhibitAreaType(1);
            checkCenter(prohibitArea);
            prohibitAreaList.add(prohibitArea);
        }
        List<String> prohibitAreaNameList = checkProhibitAreaName(accountId, prohibitAreaList);
        if (!CollectionUtils.isEmpty(prohibitAreaNameList)){
            throw new BaseException("禁停区名称重复" + prohibitAreaNameList);
        }
        batchInsert(prohibitAreaList);
    }
    /**
     * 新增时检查禁停区名称重复: 1、列表不重复，2、新增数据和数据库不重复
     *
     * @param accountId        区域id
     * @param prohibitAreaList 禁停区集合
     */
    private List<String> checkProhibitAreaName(Integer accountId, List<ProhibitArea> prohibitAreaList) {
        Map<String, List<ProhibitArea>> groupedByName = prohibitAreaList.stream()
                .collect(Collectors.groupingBy(ProhibitArea::getName));
        List<String> duplicateNames = groupedByName.entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(duplicateNames)){
            return duplicateNames;
        }
        List<String> prohibitAreaNameList = prohibitAreaDao.checkProhibitAreaName(accountId, prohibitAreaList);
        if (!CollectionUtils.isEmpty(prohibitAreaNameList)){
            return prohibitAreaNameList;
        }
        return null;
    }

    @Override
    public void batchInsert(List<ProhibitArea> prohibitAreaList) {
        prohibitAreaDao.batchInsert(prohibitAreaList);
    }

    @Override
    public Result<Object> getBatchUpdateProhibitAreaTemplate(Integer accountId, Integer type, Integer mapType) {

        ExcelUtil<BatchUpdateProhibitArea> excelUtil = new ExcelUtil<>(BatchUpdateProhibitArea.class);
        List<BatchUpdateProhibitArea> list = new ArrayList<>();
        if (type == 1) {
            list = prohibitAreaDao.getBatchUpdateByAccountId(accountId);
        }

        list.forEach(v -> {
            if (Objects.equals(MapConstant.MAP_ORI, mapType)) {
                v.setExportPoints(v.getPoints());
            } else {
                v.setExportPoints(v.getPointsC());
            }
        });

        return Result.success(excelUtil.exportExcel(list, "批量修改禁停区信息模板").getData());
    }

    @Override
    @Transactional
    public Result<String> batchUpdateProhibitArea(MultipartFile file, Integer accountId, Integer mapType) {
        List<ProhibitArea> updateList = new ArrayList<>();
        ExcelReader reader = cn.hutool.poi.excel.ExcelUtil.getReader(Objects.requireNonNull(AdaPayUtil.MultipartFileToFile(file)));
        reader.setCellEditor(new CellEditor() {
            @Override
            public Object edit(Cell cell, Object value) {
                return value != null ? value.toString() : "";
            }
        });
        try {
            List<Map<String, Object>> readAll = reader.readAll();
            for (Map<String, Object> prohibitAreaMap : readAll) {
                String prohibitAreaId = (String) Optional.ofNullable(prohibitAreaMap.get("禁停区id")).orElse("");
                if (StringUtils.isBlank(prohibitAreaId)) {
                    throw new BaseException("excel文件数据有误,禁停区id不能为空");
                }
                String prohibitAreaName = (String) Optional.ofNullable(prohibitAreaMap.get("禁停区名称")).orElse("");
                if (StringUtils.isBlank(prohibitAreaName)) {
                    throw new BaseException("excel文件数据有误,禁停区名称不能为空");
                }
                String allowRange = Optional.ofNullable(prohibitAreaMap.get("误差范围（米）")).orElse("").toString();
                if (StringUtils.isBlank(allowRange)) {
                    throw new BaseException("excel文件数据有误,误差范围不能为空");
                }
                String importPoints = (String) Optional.ofNullable(prohibitAreaMap.get("禁停区经纬度集合")).orElse("");
                if (StringUtils.isBlank(importPoints)) {
                    throw new BaseException("excel文件数据有误,禁停区经纬度不能为空");
                }
                ProhibitArea prohibitArea = new ProhibitArea();
                prohibitArea.setProhibitAreaId(Integer.valueOf(prohibitAreaId));
                prohibitArea.setAccountId(accountId);
                prohibitArea.setName(prohibitAreaName);
                prohibitArea.setAllowRange(Integer.parseInt(allowRange));
                prohibitArea.setPoints(importPoints);
                if (Objects.equals(MapConstant.MAP_ORI, mapType)) {
                    calibrateService.calProhibitArea(prohibitArea, MapConstant.MAP_ORI, MapConstant.MAP_GOOGLE);
                }else {
                    calibrateService.calProhibitArea(prohibitArea, MapConstant.MAP_GOOGLE, MapConstant.MAP_ORI);
                }
                checkCenter(prohibitArea);
                updateList.add(prohibitArea);
            }
            checkProhibitAreaNameAndProhibitAreaId(accountId, updateList);
            prohibitAreaDao.batchUpdateProhibitArea(updateList);
        } catch (Exception e){
            log.error(e.getMessage(), e);
            throw new BaseException(e.getMessage());
        } finally {
            reader.close();
        }
        return Result.success("批量修改成功");
    }

    /**
     * 修改时检查禁停区名称重复: 1、列表不重复，2、修改后区域内不重复
     *
     * @param accountId        区域id
     * @param prohibitAreaList 禁停区集合
     */
    private void checkProhibitAreaNameAndProhibitAreaId(Integer accountId, List<ProhibitArea> prohibitAreaList) {
        List<ProhibitArea> dbProhibitAreaList = prohibitAreaDao.getAll(accountId);
        Set<Integer> notExistProhibitAreaIdSet = prohibitAreaList.stream()
                .map(ProhibitArea::getProhibitAreaId)
                .collect(Collectors.toSet())
                .stream()
                .filter(prohibitAreaId -> !dbProhibitAreaList.stream()
                        .anyMatch(prohibitArea -> prohibitArea.getProhibitAreaId().equals(prohibitAreaId)))
                .collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(notExistProhibitAreaIdSet)){
            throw new BaseException("禁停区id" + notExistProhibitAreaIdSet + "不存在");
        }
        Map<String, ProhibitArea> dbNameMap = dbProhibitAreaList.stream()
                .collect(Collectors.toMap(ProhibitArea::getName, prohibitArea -> prohibitArea, (existing, replacement) -> existing));

        List<String> repeatedName = prohibitAreaList.stream()
                .filter(prohibitArea -> dbNameMap.containsKey(prohibitArea.getName()) &&
                        !dbNameMap.get(prohibitArea.getName()).getProhibitAreaId().equals(prohibitArea.getProhibitAreaId()))
                .map(ProhibitArea::getName)
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(repeatedName)) {
            throw new BaseException("禁停区名称" + repeatedName + "重复");
        }
    }

    @Override
    public ProhibitArea getByProhibitAreaName(Integer accountId, String prohibitAreaName) {
        return prohibitAreaDao.getByProhibitAreaName(accountId, prohibitAreaName);
    }

    @Override
    public List<ExportProhibitArea> selectByProhibitAreaName(Integer accountId, String prohibitAreaName) {
        return prohibitAreaDao.selectByProhibitAreaName(accountId, prohibitAreaName);
    }

    private void checkCenter(ProhibitArea prohibitArea) {
        //多边形电子围栏
        List<Point> points = GeoUtil.getPoints(prohibitArea.getPoints());
        List<Point> pointsC = GeoUtil.getPoints(prohibitArea.getPointsC());
        Point newPoint = GeoUtil.GetCenterPoint(points);
        Point newPointC = GeoUtil.GetCenterPoint(pointsC);
   /* List<Coordinate> points = GeoUtil.getCoordinate(prohibitArea.getPoints());
    List<Coordinate> pointsC = GeoUtil.getCoordinate(prohibitArea.getPointsC());
    Point newPoint = GeoUtil.getCenterOfGravityPoint4(points);
    Point newPointC = GeoUtil.getCenterOfGravityPoint4(pointsC);*/
        prohibitArea.setLaC(newPointC.getY());
        prohibitArea.setLoC(newPointC.getX());
        prohibitArea.setLa(newPoint.getY());
        prohibitArea.setLo(newPoint.getX());

    }
}
